#include "zookeeperutil.h"
#include "mprpcapplication.h"

#include <iostream>

// 全局的回调
/**
 * @brief 全局的回调，zkserver 给 zkclient 的通知
 * @param zh 当前客户端的句柄
 * @param type 回调的消息类型
 * @param
*/
void globle_watcher(zhandle_t* zh, int type, int state, const char* path, void* watcherCtx)
{
    if(type == ZOO_SESSION_EVENT)  // ZOO_SESSION_EVENT，和会话相关的消息类型
    {
        if(state == ZOO_CONNECTING_STATE)  // zkserver 和 zkclient 连接成功
        {
            sem_t* sem = (sem_t*)zoo_get_context(zh);  // 从指定的句柄上获取信号量
            sem_post(sem);  // 增加信号量，Start()中wait的部分起来了
        }
    }
}

ZkClient::ZkClient() : m_zhandle(nullptr)
{
}

ZkClient::~ZkClient()
{
    if(m_zhandle != nullptr)
    {
        zookeeper_close(m_zhandle);  // 关闭句柄，释放资源
    }
}

// zkclient 启动连接 zkserver
void ZkClient::Start()
{
    std::string host = MprpcApplication::GetInstance().GetConfig().Load("zookeeperip");
    std::string port = MprpcApplication::GetInstance().GetConfig().Load("zookeeperport");
    std::string connstr = host + ":" + port;

    /**
     * zookeeper_mt: 多线程版本
     * zookeeper的API客户端程序提供了三个线程
     *  API调用线程
     *  网络IO线程 pthread_create poll
     *  watcher 回调线程 zkserver给zkclient通知
     * 
     * 返回值为空表示发起的动作没有产生，传入的参数有问题
     * zookeeper_init 函数返回表示初始化内存资源成功了，网络连接发送到zkserver上还没有响应，这是一个异步的操作
    */
    // ip:port，回调函数, 超时时间
    m_zhandle = zookeeper_init(connstr.c_str(), globle_watcher, 30000, nullptr, nullptr, 0);

    if(m_zhandle == nullptr)  // 这里是否空表示句柄的内存开辟初始化有没有成功
    {
        std::cout << "zookeeper_init error!" << std::endl;
        // 挂在这里可能是因为 java 
        exit(EXIT_FAILURE);
    }

    sem_t sem;
    sem_init(&sem, 0, 0);
    zoo_set_context(m_zhandle, &sem);  // 给指定的句柄设置信号量

    sem_wait(&sem);
    std::cout << "zookeeper_init error!" << std::endl;
}

/**
 * @brief 在 zkserver 上根据指定的 path 创建 znode 节点
 * @param state，节点状态 ZOO_EPHEMERAL(临时性节点) ZOO_SEQUENCE(永久性节点)
*/

void ZkClient::Create(const char* path, const char* data, int datalen, int state)
{
    char path_buffer[128] = {0};
    int buffer_len = sizeof(path_buffer);
    int flag;

    flag = zoo_exists(m_zhandle, path, 0, nullptr);  // 判断 path 表示的节点是否存在，存在就不重复创建
    if(flag == ZNONODE)  // path 表示的节点不存在
    {
        // 创建指定 path 的 znode 节点
        flag = zoo_create(m_zhandle, path, data, datalen, &ZOO_OPEN_ACL_UNSAFE, state, path_buffer, buffer_len);
        if(flag == ZOK)
        {
            std::cout << "znode create success ... path: " << path << std::endl;
        }
        else
        {
            std::cout << "flag: " << flag << std::endl;
            std::cout << "znode create error ... path: " << path << std::endl;
            exit(EXIT_FAILURE);
        }
    }
}

// 根据参数指定的 znode 路径，获取 znode 节点的值
std::string ZkClient::GetData(const char* path)
{
    char buffer[64] = {0};
    int buffer_len = sizeof(buffer);
    int flag = zoo_get(m_zhandle, path, 0, buffer, &buffer_len, nullptr);
    if(flag != ZOK)
    {
        std::cout << "get znode error ... path: " << path << std::endl;
        return "";
    }
    else
    {
        return buffer;
    }
}